package mysql

import (
	"fmt"

	"gorm.io/gorm"
	"gorm.io/gorm/clause"

	"mxshop_srvs/goods_srv/model"
	proto "mxshop_srvs/goods_srv/proto/gen/v1"
	mql "mxshop_srvs/shared/mysql"
)

type GoodsDAO interface {
	GetCategoryIDs(topCategory, categoryLevel int32) ([]interface{}, error)
	GetCategory(id int32) (Category, error)
	GetGoodsList([]int32) ([]model.Goods, error)
}

func NewGoodsDAO(db *gorm.DB) GoodsDAO {
	return &goodsGORM{db: db}
}

type goodsGORM struct {
	db *gorm.DB
}

func (g *goodsGORM) GetCategoryIDs(topCategory, categoryLevel int32) ([]interface{}, error) {
	db := g.db.Select("id").Model(model.Category{})

	switch categoryLevel {
	case 1:
		subQueryIDs := g.db.Select("id").Model(model.Category{}).Where("parent_category_id = ?", topCategory)
		db = db.Where("parent_category_id IN (?)", subQueryIDs)
	case 2:
		db = db.Where(model.Category{ParentCategoryID: topCategory})
	case 3:
		db = db.Where(model.Category{BaseModel: model.BaseModel{ID: topCategory}})
	default:
		return nil, gorm.ErrInvalidField
	}

	type CategoryIDs struct {
		ID int32 `json:"id"`
	}

	var category []CategoryIDs
	err := db.Find(&category).Error
	if err != nil {
		return nil, err
	}
	categoryIDs := make([]interface{}, 0, len(category))
	for _, good := range category {
		categoryIDs = append(categoryIDs, good.ID)
	}
	return categoryIDs, err
}

type Category struct {
	Level int32 `form:"level" json:"level"`
}

func (g *goodsGORM) GetCategory(id int32) (Category, error) {
	var category Category
	return category, g.db.Model(&model.Category{}).First(&category, id).Error
}

func (g *goodsGORM) GetGoodsList(goodsIDs []int32) ([]model.Goods, error) {
	var goods []model.Goods
	if len(goodsIDs) == 0 {
		return goods, g.db.Preload("Category").Preload("Brands").Scopes(Paginate(1, 10)).Find(&goods, goodsIDs).Error
	}
	return goods, g.db.Preload("Category").Preload("Brands").Find(&goods, goodsIDs).Error
}

func (g *Gorm) CreateGoods(req *proto.CreateGoodsInfo) (goodsID int32, err error) {
	var goods = model.Goods{
		BaseModel: model.BaseModel{
			ID:        mql.NewObjID,
			CreatedAt: mql.CreatedAt(),
		},
		CategoryID:      req.CategoryId,
		BrandsID:        req.BrandId,
		OnSale:          req.OnSale,
		ShipFree:        req.ShipFree,
		IsNew:           req.IsNew,
		IsHot:           req.IsHot,
		Name:            req.Name,
		GoodsSn:         req.GoodsSn,
		MarketPrice:     req.MarketPrice,
		ShopPrice:       req.ShopPrice,
		GoodsBrief:      req.GoodsBrief,
		Images:          req.Images,
		DescImages:      req.DescImages,
		GoodsFrontImage: req.GoodsFrontImage,
	}
	result := g.db.Create(&goods)
	return goods.ID, result.Error
}

func (g *Gorm) GoodsList(req *proto.GoodsFilterRequest, categoryLevel int32) (count int64, goods []model.Goods, err error) {
	db := g.db.Model(model.Goods{})
	if req.KeyWords != "" {
		db = db.Where("name LIKE ?", fmt.Sprintf("%%%s%%", req.KeyWords))
	}
	if req.IsHot {
		db = db.Where(model.Goods{IsHot: true})
	}
	if req.IsNew {
		db = db.Where(model.Goods{IsNew: true})
	}

	if req.PriceMin > 0 {
		db = db.Where("shop_price>=?", req.PriceMin)
	}
	if req.PriceMax > 0 {
		db = db.Where("shop_price<=?", req.PriceMax)
	}

	if req.Brand > 0 {
		db = db.Where("brands_id=?", req.Brand)
	}

	if req.TopCategory > 0 {
		switch categoryLevel {
		case 1:
			// var categoryIDs []int
			subQueryIDs := g.db.Select("id").Model(model.Category{}).Where("parent_category_id = ?", req.TopCategory)
			subQuery := g.db.Select("id").Model(model.Category{}).Where("parent_category_id IN (?)", subQueryIDs)
			db = db.Where("category_id IN (?)", subQuery)
		case 2:
			subQuery := g.db.Select("id").Model(model.Category{}).Where(model.Category{ParentCategoryID: req.TopCategory})
			db = db.Where("category_id IN (?)", subQuery)
		case 3:
			subQuery := g.db.Select("id").Model(model.Category{}).Where(model.Category{BaseModel: model.BaseModel{ID: req.TopCategory}})
			db = db.Where("category_id IN (?)", subQuery)
		default:
			return 0, nil, gorm.ErrInvalidField
		}
	}

	db.Count(&count)

	result := db.Preload("Category").Preload("Brands").Scopes(Paginate(int(req.Pages), int(req.PagePerNums))).Find(&goods)
	if result.Error != nil {
		return 0, nil, result.Error
	}
	return
}

func (g *Gorm) BatchGetGoods(IDs []int32) (count int64, goods []model.Goods, err error) {
	result := g.db.Preload("Category").Preload("Brands").Find(&goods, IDs)
	return result.RowsAffected, goods, result.Error
}

func (g *Gorm) GetGoodsByID(ID int32) (goods model.Goods, err error) {
	return goods, g.db.Preload("Category").Preload("Brands").First(&goods, ID).Error
}

func (g *Gorm) UpdateGoods(goods model.Goods, req *proto.CreateGoodsInfo) (newGoods model.Goods, err error) {
	if req.CategoryId > 0 {
		goods.Category = model.Category{}
		goods.CategoryID = req.CategoryId
	}
	if req.BrandId > 0 {
		goods.Brands = model.Brands{}
		goods.BrandsID = req.BrandId
	}
	goods.Name = req.Name
	goods.GoodsSn = req.GoodsSn
	goods.MarketPrice = req.MarketPrice
	goods.ShopPrice = req.ShopPrice
	goods.GoodsBrief = req.GoodsBrief
	goods.ShipFree = req.ShipFree
	goods.Images = req.Images
	goods.DescImages = req.DescImages
	goods.GoodsFrontImage = req.GoodsFrontImage
	goods.IsNew = req.IsNew
	goods.IsHot = req.IsHot
	goods.OnSale = req.OnSale

	err = g.db.Transaction(func(tx *gorm.DB) error {
		// tx.Model(&goods).Association("Category").Replace(model.Category{
		// 	BaseModel: model.BaseModel{ID: req.CategoryId},
		// })
		// tx.Model(&goods).Association("Brands").Replace(model.Brands{
		// 	BaseModel: model.BaseModel{ID: req.BrandId},
		// })
		return tx.Clauses(clause.Returning{}).Save(&goods).Error
	})
	return goods, err
}

func (g *Gorm) DeleteGoods(ID int32) (int64, error) {
	result := g.db.Delete(&model.Goods{}, ID)
	return result.RowsAffected, result.Error
}
